package com.aeye.mbr.ws;

import com.aeye.mbr.common.base.BaseResult;
import com.aeye.mbr.common.base.constants.ApplicationConstants;
import com.aeye.mbr.common.base.constants.ErrorCodeConstants;
import com.aeye.mbr.common.exception.AlgorithmRuntimeException;
import com.aeye.mbr.common.exception.ServiceException;
import com.aeye.mbr.common.listener.ApplicationContextListener;
import com.aeye.mbr.common.util.PropertiesFileUtil;
import com.aeye.mbr.common.util.RedisUtil;
import com.aeye.mbr.common.util.SpringContextUtil;
import com.aeye.mbr.ws.api.BaseResp;
import com.aeye.mbr.ws.api.Head;
import com.aeye.mbr.ws.api.InputData;
import com.aeye.mbr.ws.api.OutPutData;
import com.aeye.mbr.ws.service.RestfullService;
import com.aeye.mbr.ws.util.JSONUtils;
import com.aeye.mbr.ws.util.RedisImgUtils;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.net.util.Base64;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.AsyncContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;

public class ServiceExecutor implements Runnable{
	private static Logger log = LoggerFactory.getLogger(ServiceExecutor.class);
	// 全局会话key
    private final static String ZHENG_UPMS_SERVER_SESSION_ID = "zheng-upms-server-session-id";

	private AsyncContext ctx = null;
	public ServiceExecutor(AsyncContext ctx){
		this.ctx = ctx;
	}

	private String excute(String input){
		InputData inputData = null;
		inputData = JSONObject.parseObject(input, InputData.class);
		
		BaseResp br = validAuth(inputData);
		
		if (br!=null)
		{
			OutPutData outPut = new OutPutData();
			outPut.setFnId(inputData.getFnId());
			outPut.setHead(inputData.getHead());
			outPut.setData(br);
			return JSONUtils.toJSONString(outPut);
		}

		//建模认证这两个接口里面如果pics数组传的是图片id，要先从redis中取出图片再进行业务处理
		if("backendRecognize".equals(inputData.getFnId())){
			JSONArray data = inputData.getData().getJSONArray("compareData");
			String outPut = retrievePic(inputData, data);
			if (outPut != null) {
				return outPut;
			}
		}
		if("backendCreateModel".equals(inputData.getFnId())){
			JSONArray data = inputData.getData().getJSONArray("modelData");
			String outPut = retrievePic(inputData, data);
			if (outPut != null) {
				return outPut;
			}
		}

		OutPutData outPut = invoke(inputData);
		outPut.setFnId(inputData.getFnId());
		outPut.setHead(inputData.getHead());
		String result = JSONUtils.toJSONString(outPut);

		return result;
	}

	private String retrievePic(InputData inputData, JSONArray data) {
		if(null != data){
            for(Object o:data){
                JSONArray pics = ((JSONObject)o).getJSONArray("pics");
                for(int i = 0; i < pics.size(); i++){
                    String pic = pics.getString(i);
                    if(!StringUtils.isEmpty(pic) && pic.startsWith(RedisImgUtils.UPMS_IMG_ID_PREFIX)){
                        byte[] picBytes = RedisImgUtils.getRedisImg(pic);
                        if(null != picBytes){
                            pics.set(i, Base64.encodeBase64String(picBytes));
                        }else{
                            //TODO 找不到图片
                            BaseResp resp = BaseResp.error(BaseResult.fail("找不到图片:" + pic));
                            OutPutData outPut = new OutPutData();
                            outPut.setFnId(inputData.getFnId());
                            outPut.setHead(inputData.getHead());
                            outPut.setData(resp);
                            return JSONUtils.toJSONString(outPut);
                        }
                    }
                }
            }
        }
		return null;
	}

	/**
	 * 校验权限
	 * @param inputData
	 * @return
	 */
	private BaseResp validAuth(InputData inputData) {
		// 1.如果是接入应用，判断接入校验是否正确
		Head head = inputData.getHead();
		String userName = head.getUserId();
		if (StringUtils.isNotEmpty(head.getChannel()))
		{

		}else
		{
			// 1.判断是否已经登录
			String sessionId = head.getSessionId();
			String username = RedisUtil.get(ZHENG_UPMS_SERVER_SESSION_ID + "_" + sessionId);
	        
			if (StringUtils.isEmpty(username))
			{
				String fnIdList = PropertiesFileUtil.getInstance("intf").get("fnId.whitelist");
		        
				if (fnIdList.indexOf(inputData.getFnId()) == -1)
				{
					return BaseResp.error(ErrorCodeConstants.JSON_LOGIN);
				}
			}else{
				//校验请求是否合法
				if(!username.equals(head.getUserId()))
				{
					return BaseResp.error(ErrorCodeConstants.REQUEST_UNVALIDAT);
				}
			}


			
		}
		
		
		return null;
	}

	private boolean validateSign(String appScope, String appSecret,InputData inputData) {
		boolean returnFlag = true;

        //判断是否是外部应用
        if(ApplicationConstants.APP_SCOPE.equals(appScope)){
            String signStr = inputData.getHead().toString()+inputData.getData().toString() + appSecret;
            log.info("签名前--------"+signStr);
            MD5Generator md5 = new MD5Generator();
            try {
                String signValue = md5.generateValue(signStr);
                log.info("签名后--------"+signValue);
                if (!signValue.equals(inputData.getSign())) {
                    returnFlag = false;
                }
            } catch (OAuthSystemException e) {
                returnFlag = false;
            }
        }

        return returnFlag;
	}

	private boolean checkInvalidDate(Date appInvalidTime) {
		//判断期限是否在今天之前，在之前表示已过期
        Date now=new Date();
        long nowTime=now.getTime();
        long invaliDateTime=appInvalidTime.getTime();
        if(invaliDateTime<nowTime){
            return false;
        }
        return true;
	}

	private OutPutData invoke(InputData inputData) {
		String fnId = inputData.getFnId();
		Integer sysCode = inputData.getSysCode();
		OutPutData outPut = new OutPutData();
		outPut.setFnId(inputData.getFnId());
		outPut.setHead(inputData.getHead());
		outPut.setSign("");
		try
		{
			//如果子系统有对应的接口编码，则优先用子系统的
			ServiceInvoker invoker = ApplicationContextListener.getServiceInvoker(fnId,sysCode);
			if( invoker != null){
				Object result = invoker.invoke(inputData);
				//TODO write log here
				//dubbo 返回的数据会将对象解析成map，map中会包含class属性，需要将这些属性移除掉
				//remove class attribute
				removeClassAttribute(result);
				outPut.setData(result);
				return outPut;
			}else{
				//框架有相应接口服务，直接调用框架的服务
				outPut.setData(SpringContextUtil.getBean(RestfullService.class).invoke(inputData));
			}
		}catch(ServiceException se)
		{
			log.error(se.getMessage(),se);
			outPut.setData(BaseResp.error(se));
			return outPut;
		}catch(AlgorithmRuntimeException e)
		{
			log.error(e.getEc().getDesc());
			outPut.setData(BaseResp.error(e.getEc()));
			return outPut;
		}
		catch(Exception se)
		{
			log.error(se.getMessage(),se);
			outPut.setData(BaseResp.error(se));
			return outPut;
		}
		return outPut;
	}

	private void removeClassAttribute(Object result) {
		if(result instanceof  Map){
			((Map) result).remove("class");
			for(Object value:((Map) result).values()){
				removeClassAttribute(value);
			}
		}else if(result instanceof List){
			for(Object value:(List)result)
			{
				removeClassAttribute(value);
			}
		}
	}

	@Override
	public void run() {
		HttpServletRequest req = (HttpServletRequest)ctx.getRequest();
		HttpServletResponse resp = (HttpServletResponse)ctx.getResponse();
		resp.setContentType("text/xml;charset=UTF-8");
		resp.setCharacterEncoding("UTF-8");
		String input = getRequestMessage(req);
		if(log.isDebugEnabled())
		{
			log.debug("input:++++++++++++++++++++++++++" + input);
			long startTime = System.nanoTime();
			log.debug("============================AppService start at time :" + startTime);
		}
		String output = "";
		ServiceContext.bind(req, resp);
		try{
			String jsonResult = excute(input);
			output = setReturnMessage(req,jsonResult);
		}catch(JSONException je)
		{
			je.printStackTrace();
			OutPutData opd = new OutPutData();
			BaseResp bresp = BaseResp.error(ErrorCodeConstants.JSON_PARSE_ERROR);
			opd.setData(bresp);
			output = setReturnMessage(req,JSONUtils.toJSONString(opd));
			log.error("input:" +input);
			log.error("output:" +output);
		}
		catch(Exception e) {
			log.error(e.getMessage(), e);
			OutPutData opd = new OutPutData();
			BaseResp bresp = BaseResp.error(e);
			opd.setData(bresp);
			output = setReturnMessage(req,JSONUtils.toJSONString(opd));
			log.error("input:" +input);
			log.error("output:" +output);
		}finally{
			try {
				resp.getWriter().write(output);
			} catch (IOException e) {
				e.printStackTrace();
			}
			ServiceContext.unbind(req, resp);
			if(log.isDebugEnabled())
			{
				log.debug("output:++++++++++++++++++++++++++" + output);
				long endTime = System.nanoTime();
				log.debug("============================AppService end at time :" + endTime);
			}
			ctx.complete();
		}
	}

	/**
	 * 取得请求消息
	 * @param req
	 * @return
	 */
	public String getRequestMessage(HttpServletRequest req) {
		try {
			//获得请求信息XML
			InputStreamReader isReader = new InputStreamReader(req.getInputStream(), "UTF-8");
			BufferedReader reader = new BufferedReader(isReader);
			StringBuilder sb = new StringBuilder();
			String line = "";
			while ((line=reader.readLine())!=null){
				sb.append(line.replaceAll("&quot;", "\""));
			}
			String paramStr = sb.toString();

			String paramJson = "";
			if(isSoapContent(req))
			{
				paramJson = paramStr.substring(paramStr.indexOf("{"), paramStr.lastIndexOf("}")+1);
			}else{
				paramJson = paramStr;
			}
			return paramJson;

		} catch (Exception e) {
			log.error(e.getMessage() + e);
		}
		return null;
	}

	/**
	 * 设置返回消息
	 * @param str
	 * @return
	 */
	public String setReturnMessage(HttpServletRequest req,String str) {
		StringBuilder sb = new StringBuilder();

		if(isSoapContent(req))
		{
			sb.append("<soap:Envelope xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\">")
					.append("<soap:Body><ns1:invokewsResponse ")
					.append("xmlns:ns1=\"http://webservice.ws.mbr.aeye.com/\"><return>").append(str)
					.append("</return></ns1:invokewsResponse></soap:Body></soap:Envelope>");
		}else
		{
			sb.append(str);
		}
		return sb.toString();
	}

	private static boolean isSoapContent(HttpServletRequest request) {
		if (!"post".equalsIgnoreCase(request.getMethod())) {
			return false;
		}

		String contentType = request.getContentType();
		if (contentType == null) {
			return false;
		}
		if (contentType.toLowerCase(Locale.ENGLISH).contains("xml")) {
			return true;
		}
		return false;
	}

	private static boolean isUrlEncodeContent(HttpServletRequest request) {
		if (!"post".equalsIgnoreCase(request.getMethod())) {
			return false;
		}

		String contentType = request.getContentType();
		if (contentType == null) {
			return false;
		}
		if (contentType.toLowerCase(Locale.ENGLISH).startsWith("application/x-www-form-urlencoded")) {
			return true;
		}
		return false;
	}
}
